﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Log4Nez.Interfaces;
using System.Data.SqlClient;
using System.Reflection;
using System.Configuration;

namespace Log4Nez.Loggers
{
    internal class DataBaseLogger : ILogger
    {

        #region Private Data Members
        private Dictionary<string, string> _columns = null;//the collection that contains the mapping of message properties with table columns
        private SqlConnection _dbConnection = null;//the database connection
        private SqlCommand _dbCommand = null;//the command to execute on the database
        private string _tableName = string.Empty;//the table that will contain the log
        private PropertyInfo[] _messageProperties = null;  //the array of properties of the message class
        #endregion

        #region Ctors
        public DataBaseLogger(Dictionary<string, string> columns, string connectionString, string tableName)
        {
            //set the collection of mapping given by the factory
            this._columns = columns;
            //istantiate the connection to the database identified by the connectionString 
            this._dbConnection = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionString].ConnectionString);
            this._tableName = tableName;
        }
        #endregion

        #region Private Methods

        private void initDbCommand()
        {
            try
            {
                StringBuilder columnsStringBuilder = new StringBuilder();
                StringBuilder valuesStringBuilder = new StringBuilder();
                
                foreach (string key in _columns.Keys)
                {
                    columnsStringBuilder.AppendFormat("{0},", _columns[key]);                    
                    valuesStringBuilder.AppendFormat("@{0},", key);

                }
                StringBuilder sqlStringBuilder = new StringBuilder();
                sqlStringBuilder.AppendFormat("Insert INTO {0} ({1}) values ({2}) ", _tableName, columnsStringBuilder.ToString(), valuesStringBuilder.ToString());
                sqlStringBuilder = sqlStringBuilder.Replace(",)", ")");
                _dbCommand = new SqlCommand(sqlStringBuilder.ToString(), _dbConnection);
                _dbCommand.Prepare();
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        #endregion


        public bool Write(System.Diagnostics.TraceLevel level, object message, params string[] formatParams)
        {
            try
            {
                //Open the Connection
                if (this._dbConnection.State !=System.Data.ConnectionState.Open)
                    _dbConnection.Open();
                
                //Initialize the command
                if (_dbCommand == null)
                    initDbCommand();
                
                //Get all properties of message class
                if (_messageProperties == null)
                    _messageProperties = message.GetType().GetProperties();
                
                //Create a collectionof parameters for _dbCommand
                List<SqlParameter> parameterCollection = new List<SqlParameter>();
                
                //for each key in _columns dictionary
                foreach (string key in _columns.Keys)
                    //add it to parameterCollection with the value find in the message object
                    parameterCollection.Add(new SqlParameter(key, _messageProperties.First(p => p.Name == key).GetValue(message, null)));
                
                //Clear the collection of parameters already added to the command and add the new collection of parameters value
                _dbCommand.Parameters.Clear();
                _dbCommand.Parameters.AddRange(parameterCollection.ToArray());

                //Execute the command returning true if the query is executed
                return _dbCommand.ExecuteNonQuery() > 0;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public System.Diagnostics.TraceLevel LoggerLevel
        {
            get;
            set;
        }
    }
}
